共计 4478 个字符,预计需要花费 12 分钟才能阅读完成。
前言
首先final
从字面解释,表示最终的,最后的,java编程中通常指“这是无法改变的”;下面谈一谈final
使用的3种场景:数据、方法、类!
final修饰数据
数据被final
关键字修饰的时候,就是告知编译器这一块数据是恒定不变的,我们称之为常量;
tips:常量又有以下区分
1.编译时常量;
2.运行时常量(在运行时才会被初始化,而且你也不希望它在后续使用过程中被改变);
编译时常量,在编译时执计算式,相对地减轻了运行时的负担,这类常量必须是基本数据类型,必须在定义的时候进行赋值!
import java.util.Random;
class Value {
String id;
public Value(String id) {
System.out.println("Value " + id + " Constructor.");
this.id = id;
}
@Override
public String toString() {
return "Value{" +
"id='" + id + '\'' +
'}';
}
}
/**
* @author plm
* @create 2021/3/6 18:28
*/
public class FinalTest {
private static Random random = new Random(47);
// 编译时常量
private final int valueOne = 1;
private static final int VALUE_TWO = 2;
public static final int VALUE_THREE = 3;
// 运行时常量
private final int valueFour = random.nextInt(20);
private static final int VALUE_FIVE = random.nextInt(20);
// 引用类型
private Value v1 = new Value("v1");
private final Value v2 = new Value("v2");
private static final Value V_3 = new Value("v3");
// 数组
private final int[] arr1 = {1, 2, 3};
@Override
public String toString() {
return "FinalTest{" +
"valueFour=" + valueFour +
"VALUE_FIVE=" + VALUE_FIVE +
'}';
}
public static void main(String[] args) {
FinalTest f1 = new FinalTest();
//! f1.valueOne++; // Cannot assign a value to final variable 'valueOne
f1.v2.id = "v2.0";
f1.v1 = new Value("v1.0");
//! f1.arr1 = new int[2]; // Cannot assign a value to final variable 'arr1'
f1.arr1[1]++;
//! f1.v2 = new Value("v2.1"); // Cannot assign a value to final variable 'v2'
//! f1.V_3 = new Value("v3.0"); // Cannot assign a value to final variable 'V_3'
System.out.println(f1);
System.out.println("===============================");
FinalTest f2 = new FinalTest();
System.out.println(f1);
System.out.println(f2);
}
}
/* 输出
Value v3 Constructor.
Value v1 Constructor.
Value v2 Constructor.
Value v1.0 Constructor.
FinalTest{valueFour=15VALUE_FIVE=18}
===============================
Value v1 Constructor.
Value v2 Constructor.
FinalTest{valueFour=15VALUE_FIVE=18}
FinalTest{valueFour=13VALUE_FIVE=18}
*/
总结
编译期常量有valueOne、VALUE_TWO、VALUE_THREE,平常开发中常见的是后两者,尤其是变量名以全大写字母命名;
运行时常量有valueFour、VALUE_FIVE;区别在于是否static
修饰,静态只在类加载时初始化一次就不会再变,非静态每次创建对象都会初始化一次,对象不同就可能不同,同一个对象不会再变化!
引用类型常量有v1、v2、V_3和数组arr1,仅仅是引用不变(不可以在指向其他引用类型数据),但是该数据内部属性可做修改;
空白final
指的是某一属性被final
修饰而没有给定初始值;
/**
* @author plm
* @create 2021/3/6 19:07
*/
class Pro {
private int i;
public Pro(int i) {
this.i = i;
}
}
public class FinalBlank {
private final int i = 0;
private final int j; // 如果没有初始化,编译器会提示:Variable 'j' might not have been initialized
private final Pro pro;
public FinalBlank() {
j = 0;
pro = new Pro(2);
}
public FinalBlank(int j, Pro pro) {
this.j = j;
this.pro = pro;
}
public static void main(String[] args) {
new FinalBlank();
new FinalBlank(2, new Pro(2));
}
}
总结
编译器会确保
final
修饰的数据在使用前被初始化,否则编译工具就会提示Variable ‘j’ might not have been initialized;
空白final提供了很大的灵活性,可以做到因对象而异;
final方法入参
方法入参亦可使用final
关键字修饰的,同样意味着你将无法在方法体中对其进行修改(基本数据类型无法修改值,引用数据类型无法修改引用);
final修饰方法
final
修饰方法,目前主要有两个原因:
1.把方法锁定,防止任何继承类的重写导致其含义被修改;
2.提高执行效率,在早期的Java版本(新版本虚拟机可以自行优化,不再需要单独指定final
方法)中,遇到调用final方法,编译器就会将该调用转成内嵌调用;
内嵌调用机制:将参数压入栈,跳至方法代码处并执行,然后跳回并清理栈中数据,处理返回值,并且以方法体中的实际代码的副本来代替方法调用,这将消除方法调用的开销!
/**
* @author plm
* @create 2021/3/6 21:31
*/
class WithFinals {
private final void f() {
System.out.println("WithFinals.f()");
}
private void g() {
System.out.println("WithFinals.g()");
}
}
class OverridePrivate extends WithFinals {
//! @Override // Method does not override method from its superclass
private final void f() {
System.out.println("OverridePrivate.f()");
}
//! @Override // Method does not override method from its superclass
private void g() {
System.out.println("OverridePrivate.g()");
}
}
class OverridePrivate2 extends OverridePrivate {
//! @Override // Method does not override method from its superclass
public final void f() {
System.out.println("OverridePrivate2.f()");
}
//! @Override // Method does not override method from its superclass
public void g() {
System.out.println("OverridePrivate2.g()");
}
}
public class FinalOveride {
public static void main(String[] args) {
OverridePrivate2 overridePrivate2 = new OverridePrivate2();
overridePrivate2.f();
overridePrivate2.g();
OverridePrivate overridePrivate = new OverridePrivate();
/*
* Ambiguous method call. Both
* f() in OverridePrivate
* and
* f() in WithFinals match
*/
//! overridePrivate.f();
/*
* Ambiguous method call. Both
* g() in OverridePrivate
* and
* g() in WithFinals match
*/
//! overridePrivate.g();
}
}
/* 输出
OverridePrivate2.f()
OverridePrivate2.g()
*/
可以给private方法添加
final
修饰词,虽然这无意义(类中所有private
方法都隐式的指定为final
的);
如果你试图重写一个private
方法(隐含final
的),当你加了校验注解@Override
就会发现,编译器报错Method does not override method from its superclass;
final修饰类
如果将某个类指定为final,即表明你不希望该类被继承,该类也不需要做任何改动;如String
类
/**
* @author plm
* @create 2021/3/6 21:50
*/
final class FinalPapa {
int i = 1;
int j = 2;
String s = "1";
void f() {
System.out.println("FinalPapa.f()");
}
}
//! class FutureFinal extends FinalPapa {} // Cannot inherit from final 'FinalPapa'
final
类中所有方法都隐式地被final
修饰,无法重写它们;
也同样可以添加显示final
修饰词,虽然这么做毫无意义;
最后
final修饰词的使用,日常主要集中在变量修饰